package pkg

import (
	nacos "github.com/go-kratos/kratos/contrib/config/nacos/v2"
	"github.com/go-kratos/kratos/v2/config"
	"github.com/go-kratos/kratos/v2/config/env"
	"github.com/go-kratos/kratos/v2/config/file"
	_ "github.com/joho/godotenv/autoload"
	"github.com/mitchellh/mapstructure"
	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"github.com/nacos-group/nacos-sdk-go/vo"
	"github.com/spf13/viper"
	"gopkg.in/yaml.v3"
	"hello-kratos/kratos-server/internal/conf"
)

func LoadConf(path string) *conf.Bootstrap {
	bc := new(conf.Bootstrap)
	// 先加载本地配置
	localConf(path, bc)
	// 再加载 nacos 配置
	nacosConf(bc)
	return bc
}

func localConf(path string, bc *conf.Bootstrap) {
	c := config.New(
		config.WithSource(
			env.NewSource(), // 读取环境变量
			file.NewSource(path),
		),
	)
	defer func(c config.Config) {
		_ = c.Close()
	}(c)
	if err := c.Load(); err != nil {
		panic(err)
	}
	m := make(map[string]any, 0)
	if err := c.Scan(&m); err != nil {
		panic(err)
	}
	// 使用 viper 管理
	if err := viper.MergeConfigMap(m); err != nil {
		panic(err)
	}
	if err := mapstructure.Decode(m, bc); err != nil {
		panic(err)
	}
}

func nacosConf(bc *conf.Bootstrap) {
	sources := make([]config.Source, 0, len(bc.Nacos.Config.Shared)+1)
	sc := []constant.ServerConfig{
		*constant.NewServerConfig(bc.Nacos.Config.Server.Addr, bc.Nacos.Config.Server.Port),
	}
	// 主配置文件
	mcc := constant.ClientConfig{
		NamespaceId:         bc.Nacos.Config.NamespaceId,
		Username:            bc.Nacos.Config.Server.Username,
		Password:            bc.Nacos.Config.Server.Password,
		TimeoutMs:           5000,
		NotLoadCacheAtStart: true,
		LogDir:              "/tmp/nacos/log",
		CacheDir:            "/tmp/nacos/cache",
	}
	mcli, err := clients.NewConfigClient(
		vo.NacosClientParam{
			ClientConfig:  &mcc,
			ServerConfigs: sc,
		},
	)
	if err != nil {
		panic(err)
	}

	major := nacos.NewConfigSource(mcli, nacos.WithGroup(bc.Nacos.Config.Group), nacos.WithDataID(bc.Nacos.Config.DataId))
	sources = append(sources, major)
	for _, v := range bc.Nacos.Config.Shared {
		cc := constant.ClientConfig{
			NamespaceId:         v.NamespaceId,
			Username:            bc.Nacos.Config.Server.Username,
			Password:            bc.Nacos.Config.Server.Password,
			TimeoutMs:           5000,
			NotLoadCacheAtStart: true,
			LogDir:              "/tmp/nacos/log",
			CacheDir:            "/tmp/nacos/cache",
		}
		client, err := clients.NewConfigClient(
			vo.NacosClientParam{
				ClientConfig:  &cc,
				ServerConfigs: sc,
			},
		)
		if err != nil {
			panic(err)
		}
		source := nacos.NewConfigSource(client, nacos.WithGroup(v.Group), nacos.WithDataID(v.DataId))
		sources = append(sources, source)
	}

	c := config.New(config.WithSource(sources...))
	defer func() {
		_ = c.Close()
	}()
	// 加载远程配置
	if err = c.Load(); err != nil {
		panic(err)
	}
	cm := make(map[string]any, 0)
	// 将读取到的配置放到 map 中
	if err = c.Scan(&cm); err != nil {
		panic(err)
	}
	data := make(map[string]any, len(bc.Nacos.Config.Shared)) // 用于存放共享配置
	// 此时共享配置中的值是字符串，需要用 yaml 转换为 map
	for _, v := range bc.Nacos.Config.Shared {
		if s, ok := cm[v.DataId]; ok {
			m := make(map[string]any, 0)
			if err = yaml.Unmarshal([]byte(s.(string)), &m); err != nil {
				panic(err)
			}
			data[v.DataId] = m
			delete(cm, v.DataId)
		}
	}
	cm["data"] = data
	// 合并配置，使之覆盖
	if err = viper.MergeConfigMap(cm); err != nil {
		panic(err)
	}
	if err = mapstructure.Decode(cm, bc); err != nil {
		panic(err)
	}
}
